import re
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
from src.loader.web_loader import load_web_content
from src.utils import file_operate

# 网页常见噪声标签（导航、广告、评论等）
noise_tags = [
    "header", "nav", "footer", "aside", "script", "style",
    "form", "iframe", "svg", "canvas"
]
# 网页常见内容容器类名（用于精准定位正文）
content_container_classes = [
    "article-content", "post-content", "blog-content",
    "main-content", "article-body", "entry-content"
]
# 代码语言映射（从 <code> class 提取语言）
code_lang_map = {
    "go": ["go", "golang"],
    "python": ["python", "py"],
    "java": ["java"],
    "javascript": ["js", "javascript"],
    "html": ["html"],
    "css": ["css"],
    "bash": ["bash", "shell", "sh"]
}

def clean_html(soup):
    """步骤2：清洗HTML，移除噪声内容"""
    # 1. 移除通用噪声标签
    for tag in noise_tags:
        for elem in soup.find_all(tag):
            elem.decompose()

    # 2. 移除带特定类名的噪声元素（广告、评论等）
    noise_classes = [
        "ad", "advertisement", "comment", "related", "recommend",
        "share", "social", "copyright", "author-info", "toc"  # 目录通常单独处理
    ]
    for cls in noise_classes:
        for elem in soup.find_all(class_=re.compile(cls, re.IGNORECASE)):
            elem.decompose()

    # 3. 定位核心内容容器（优先使用常见内容类名）
    content_container = None
    for cls in content_container_classes:
        content_container = soup.find(class_=re.compile(cls, re.IGNORECASE))
        if content_container:
            break
    # 若未找到容器，默认使用<body>
    if not content_container:
        content_container = soup.find("body")
        if not content_container:
            raise ValueError("无法定位网页内容容器")

    return content_container

def parse_content(container, base_url):
    """步骤3：解析内容，提取结构化数据（标题、段落、代码块等）"""
    elements = []
    # 提取文章标题（优先找容器外的<h1>，通常在页面顶部）
    title = container.find_parent().find("h1")
    if title:
        elements.append(("title", title.get_text(strip=True)))

    # 遍历容器内所有元素，提取结构化内容
    for elem in container.descendants:
        if elem.name is None:
            continue  # 跳过文本节点

        # 1. 标题（h1-h6，网页通常h1是主标题，h2-h4是章节）
        if elem.name in ["h2", "h3", "h4", "h5", "h6"]:
            level = int(elem.name[1:])  # h2 → 2
            text = elem.get_text(strip=True)
            if text:
                elements.append(("heading", (level, text)))

        # 2. 段落
        elif elem.name == "p":
            text = _parse_inline_elements(elem, base_url)
            if text:
                elements.append(("paragraph", text))

        # 3. 代码块（<pre><code> 组合，网页标准写法）
        elif elem.name == "pre":
            code_elem = elem.find("code")
            if code_elem:
                code_content = code_elem.get_text()
                lang = _detect_code_lang(code_elem)
                elements.append(("code_block", (lang, code_content)))

        # 4. 列表（有序/无序）
        elif elem.name in ["ul", "ol"]:
            list_type = "unordered" if elem.name == "ul" else "ordered"
            items = []
            for li in elem.find_all("li", recursive=False):  # 不处理嵌套列表
                item_text = _parse_inline_elements(li, base_url)
                if item_text:
                    items.append(item_text)
            if items:
                elements.append(("list", (list_type, items)))

        # 5. 图片
        elif elem.name == "img":
            src = elem.get("src") or elem.get("data-src")  # 处理懒加载图片
            if src:
                src = urljoin(base_url, src)  # 转换为绝对路径
            alt = elem.get("alt", "技术图片")
            elements.append(("image", (alt, src)))

        # 6. 链接（单独的<a>标签，非段落内联）
        elif elem.name == "a" and not elem.find_parent("p"):
            text = elem.get_text(strip=True)
            href = urljoin(base_url, elem.get("href", ""))
            if text and href:
                elements.append(("link", (text, href)))

    """
    elements 内容结构展示示例：
        [
            ("title", "Java反射机制详解"),
            ("heading", (2, "什么是反射")),
            ("paragraph", "Java反射机制是在程序运行过程中，允许程序动态获取类的信息..."),
            ("code_block", ("java", "public class Test {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, Reflection!\");\n    }\n}")),
            ("list", ("unordered", ["反射提供运行时获取类信息的能力", "支持动态调用方法和构造对象"])),
            ("image", ("Java反射示意图", "https://example.com/reflection-diagram.png")),
            ("link", ("了解更多", "https://docs.oracle.com/javase/tutorial/reflect/index.html"))
        ]
    """
    return elements

def _parse_inline_elements(elem, base_url):
    """解析行内元素（链接、加粗、斜体等）"""
    text = ""
    for child in elem.contents:
        if isinstance(child, str):
            # 普通文本，处理空格和特殊字符
            text += child.strip() + " "
        else:
            # 处理标签元素
            if child.name == "a":
                link_text = child.get_text(strip=True)
                href = urljoin(base_url, child.get("href", ""))
                text += f"[{link_text}]({href}) "
            elif child.name in ["strong", "b"]:
                text += f"**{child.get_text(strip=True)}** "
            elif child.name in ["em", "i"]:
                text += f"*{child.get_text(strip=True)}* "
            elif child.name == "code":
                # 行内代码（如 `func`）
                text += f"`{child.get_text(strip=True)}` "
    """
    返回结果示例：
        "这是一个 包含 多个 空格 的文本"
    """
    return re.sub(r"\s+", " ", text).strip()  # 合并多余空格

def _detect_code_lang(code_elem):
    """识别代码块语言"""
    classes = code_elem.get("class", [])
    for lang, keywords in code_lang_map.items():
        for kw in keywords:
            if any(re.search(kw, cls, re.IGNORECASE) for cls in classes):
                return lang
    return "code"  # 未识别语言

def generate_markdown(elements):
    """步骤4：生成Markdown内容"""
    md_lines = []
    """
    根据结构化后的内容，依据 Markdown 语法，整合为 Markdown 文件内容
    """
    for type_, data in elements:
        if type_ == "title":
            md_lines.append(f"# {data}\n")  # 标题用一级标题
        elif type_ == "heading":
            level, text = data
            md_lines.append(f"{'#' * level} {text}\n")
        elif type_ == "paragraph":
            md_lines.append(f"{data}\n")
        elif type_ == "code_block":
            lang, content = data
            md_lines.append(f"```{lang}\n{content}\n```\n")
        elif type_ == "list":
            list_type, items = data
            for i, item in enumerate(items, 1):
                prefix = "- " if list_type == "unordered" else f"{i}. "
                md_lines.append(f"{prefix}{item}\n")
            md_lines.append("\n")  # 列表后加空行
        elif type_ == "image":
            alt, src = data
            md_lines.append(f"![{alt}]({src})\n")
        elif type_ == "link":
            text, href = data
            md_lines.append(f"[{text}]({href})\n")

    # 清理多余空行
    return re.sub(r"\n{3,}", "\n\n", "".join(md_lines)).strip()

def convert(url):
    # 1. 爬取HTML
    html, base_url = load_web_content(url)
    # 2. 解析并清洗HTML，获取主要内容的容器
    soup = BeautifulSoup(html, "html.parser")
    content_container = clean_html(soup)
    # 3. 提取结构化内容
    elements = parse_content(content_container, base_url)
    # 4. 生成Markdown
    markdown = generate_markdown(elements)
    # 5. 保存文件
    filename = file_operate.save_markdown(markdown)

    return filename


# 使用示例
if __name__ == "__main__":
    # 网页URL示例（支持Go、Python等技术文章）
    url = "https://liaoxuefeng.com/books/java/reflection/class/index.html"  # Go Context 技术文章
    convert(url, "")  # 输出文件名